#include "Mesh.h"
#include "..\utils\Logger.h"

namespace ds {

Mesh::Mesh(void) {
}

Mesh::~Mesh(void) {
	if( pMaterials != NULL )
        delete[] pMaterials;

    if( pTextures ) {
        for( DWORD i = 0; i < numMaterials; i++ ) {
			if( pTextures[i] ) {
                pTextures[i]->Release();
			}
        }
        delete[] pTextures;
    }
	if ( meshXFile != NULL ) {
		meshXFile->Release();
	}
	Logger::log( "Mesh - destructed" );
}

LPD3DXMESH Mesh::getMeshFile() {
	return this->meshXFile;
}

HRESULT Mesh::loadMesh(LPCSTR fileName) {
	this->fileName = fileName;

	LPD3DXBUFFER bufXFileMaterial;
	LPD3DXBUFFER pAdjacencyBuffer = NULL;

    HRESULT hr = D3DXLoadMeshFromXA(fileName,		// load file
                      D3DXMESH_SYSTEMMEM,   // mesh in den system speicher laden
                      gEngine->getDevice(),				// Direct3D Device
                      &pAdjacencyBuffer,  
                      &bufXFileMaterial,    // materials hier
                      NULL,					// keine effect instances
                      &numMaterials,		// dieanzahl an materials in dem model
                      &meshXFile);    // put the mesh here

	if ( FAILED(hr) ) {
		Logger::error( "Mesh - could not load mesh file" );
	}
	else {
		Logger::log( "Mesh - loaded mesh file" );
	}

	meshXFile->OptimizeInplace(
                D3DXMESHOPT_COMPACT | D3DXMESHOPT_ATTRSORT | D3DXMESHOPT_VERTEXCACHE,
                ( DWORD* )pAdjacencyBuffer->GetBufferPointer(), NULL, NULL, NULL );


    D3DXMATERIAL* tempMaterials = (D3DXMATERIAL*)bufXFileMaterial->GetBufferPointer();

    // one material buffer to every mesh
    pMaterials = new D3DMATERIAL9[numMaterials];
	pTextures  = new LPDIRECT3DTEXTURE9[numMaterials];
	for( DWORD i = 0; i < numMaterials; i++ ) {
        // Copy the material
        pMaterials[i] = tempMaterials[i].MatD3D;
		pMaterials[i].Ambient = pMaterials[i].Diffuse;		
        pTextures[i] = NULL;
        if( tempMaterials[i].pTextureFilename != NULL &&
            lstrlenA( tempMaterials[i].pTextureFilename ) > 0 )
        {
			//Logger::log("Mesh - loading texture: %s\n",tempMaterials[i].pTextureFilename);
            // Create the texture
            if( FAILED( D3DXCreateTextureFromFileA( gEngine->getDevice(),
                                                    tempMaterials[i].pTextureFilename,
                                                    &pTextures[i] ) ) )
            {
                // If texture is not in current folder, try parent folder
                const CHAR* strPrefix = "..\\";
                CHAR strTexture[MAX_PATH];
                strcpy_s( strTexture, MAX_PATH, strPrefix );
                strcat_s( strTexture, MAX_PATH, tempMaterials[i].pTextureFilename );
				OutputDebugStringA( strTexture );
                // If texture is not in current folder, try parent folder
				if( FAILED( D3DXCreateTextureFromFileA( gEngine->getDevice(),
                                                        strTexture,
                                                        &pTextures[i] ) ) )
                {
                    Logger::error("Could not find texture");
                }
            }
        }
    }
		
	//if( !(meshXFile->GetFVF() & D3DFVF_NORMAL) ) {
	
		Logger::log("Recalculating normals");
        ID3DXMesh* pTempMesh;
		meshXFile->CloneMeshFVF(D3DXMESH_32BIT | D3DXMESH_VB_MANAGED,// | meshXFile->GetOptions(),
			meshXFile->GetFVF()|D3DFVF_NORMAL,
			//decl,
			gEngine->getDevice(), 
			&pTempMesh );
        D3DXComputeNormals( pTempMesh, NULL );
        SAFE_RELEASE( meshXFile );
        meshXFile = pTempMesh;
	
    //}
	
	Logger::log("Mesh - number of materials: %d",numMaterials);

	bufXFileMaterial->Release();
	return S_OK;
}

void Mesh::DeclarationElementInsert( D3DVERTEXELEMENT9* decl, UINT before, D3DDECLTYPE type, D3DDECLUSAGE usage, BYTE index,int offset)
{
    D3DVERTEXELEMENT9 element = { 0, 0, (BYTE)type, D3DDECLMETHOD_DEFAULT, (BYTE)usage, index };

    int length = before;
    if(decl[before].Stream == 255)
    {
        D3DVERTEXELEMENT9* last =  decl+(before-1);

        element.Offset = last->Offset + offset;
    }
    else
    {
        element.Offset = decl[before].Offset;

        for(int i = before; i < MAX_FVF_DECL_SIZE && decl[i].Stream != 255; i++)
        {
            decl[i].Offset = decl[i].Offset + (WORD)(offset);

            length++;
        }
    }

    assert(decl[length].Stream == 255);

    memmove(&decl[before+1], &decl[before], (length+1-before)*sizeof(D3DVERTEXELEMENT9) );


    decl[before] = element;
}

DWORD Mesh::getNumberOfMaterials() {
	return this->numMaterials;
}
D3DMATERIAL9 Mesh::getMaterial(DWORD index) {
	return this->pMaterials[index];
}
LPDIRECT3DTEXTURE9 Mesh::getTexture(DWORD index) {
	return this->pTextures[index];
}

};